package com.xiyu.service.service.restaurant;

import com.xiyu.service.vo.restaurant.dish.DishMultipleGetOutput;
import com.xiyu.service.vo.restaurant.dish.DishSingleGetOutput;
import com.xiyu.service.vo.restaurant.dish.DishListQueryDishTypeOutput;
import com.xiyu.service.model.restaurant.dish.RestaurantDishType;
import com.xiyu.service.vo.restaurant.dish.DishSingleGetDishTypeOutput;
import com.xiyu.service.model.restaurant.dish.*;
import com.xiyu.service.repository.restaurant.dish.RestaurantDishMaterialRepository;
import com.xiyu.service.repository.restaurant.dish.RestaurantDishRepository;
import com.xiyu.service.repository.restaurant.dish.RestaurantDishTypeRepository;
import com.xiyu.service.vo.restaurant.dish.DishDishTypeUpdateInput;
import com.xiyu.service.vo.restaurant.dish.DishDishTypeCreateInput;
import com.xiyu.service.vo.restaurant.dish.DishPageQueryDishTypeOutput;
import com.xiyu.service.vo.restaurant.dish.DishPageQueryDishTypeInput;
import com.xiyu.service.vo.restaurant.dish.DishUpdateInput;
import com.xiyu.service.vo.restaurant.dish.DishCreateInput;
import com.xiyu.service.vo.restaurant.dish.DishQueryInput;
import com.xiyu.service.vo.restaurant.dish.DishQueryOutput;
import org.babyfish.jimmer.DraftObjects;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.babyfish.jimmer.Page;
import java.util.*;

import org.springframework.transaction.annotation.Transactional;
import com.xiyu.service.framework.web.web.core.pojo.PageResult;
import com.xiyu.service.convert.restaurant.DishConvert;

import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;
import static com.xiyu.service.errorCode.restaurant.DishErrorCode.*;

/**
 * 餐品管理 Service 实现类
 */
@Service
@Validated
public class DishServiceImpl implements DishService {

    @Resource
    private RestaurantDishTypeRepository restaurantDishTypeRepository;

    @Resource
    private RestaurantDishRepository restaurantDishRepository;

    @Resource
    private RestaurantDishMaterialRepository restaurantDishMaterialRepository;

    @Override
    public PageResult<DishQueryOutput> query(DishQueryInput inputVO) {
        Page<RestaurantDish> dishPage = restaurantDishRepository.pageQuery(inputVO);
        List<DishQueryOutput> dishList = DishConvert.INSTANCE.queryPageOutputConvert(dishPage.getRows());
        return new PageResult<>(dishList, dishPage.getTotalRowCount());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long create(DishCreateInput inputVO) {
        Optional<RestaurantDish> opDupDish = restaurantDishRepository.findFirstByName(inputVO.getName());
        if(opDupDish.isPresent())
            throw exception(DISH_EXISTS);
        RestaurantDish newDish = DishConvert.INSTANCE.createInputConvert(inputVO);
        newDish = restaurantDishRepository.insert(newDish);
        return newDish.id();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleted(Long id) {
        Optional<RestaurantDish> opDeleteDish = restaurantDishRepository.findById(id);
        if(!opDeleteDish.isPresent()){
            throw exception(DISH_NOT_EXISTS);
        }
        restaurantDishRepository.deleteById(id);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(DishUpdateInput inputVO) {
        Optional<RestaurantDish> opDupDish = restaurantDishRepository.findFirstByNameAndIdNot(inputVO.getName(),inputVO.getId());
        if(opDupDish.isPresent())
            throw exception(DISH_EXISTS);
        for (DishUpdateInput.material material : inputVO.getMaterials()){
            if(Objects.equals(material.getOperateType(), "delete")){
                restaurantDishMaterialRepository.deleteById(material.getId());
            }else if(Objects.equals(material.getOperateType(), "new")){
                RestaurantDishMaterial newMaterial = DishConvert.INSTANCE.updateInoutConvert(material);
                restaurantDishMaterialRepository.insert(newMaterial);
            }else {
                RestaurantDishMaterial updateMaterial = DishConvert.INSTANCE.updateInoutConvert(material);
                restaurantDishMaterialRepository.update(updateMaterial);
            }
        }
        RestaurantDish updateDish = DishConvert.INSTANCE.updateInputConvert(inputVO);
        updateDish = RestaurantDishDraft.$.produce(updateDish, draft -> {
            DraftObjects.unload(draft, RestaurantDishProps.MATERIALS);
        });
        restaurantDishRepository.update(updateDish);
        return true;
    }

    @Override
    public PageResult<DishPageQueryDishTypeOutput> pageQueryDishType(DishPageQueryDishTypeInput inputVO) {
        Page<RestaurantDishType> dishTypePage = restaurantDishTypeRepository.pageQuery(inputVO);
        List<DishPageQueryDishTypeOutput> dishTypeList = DishConvert.INSTANCE.pageQueryDishTypeOutputConvert(dishTypePage.getRows());
        return new PageResult<>(dishTypeList, dishTypePage.getTotalRowCount());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long dishTypeCreate(DishDishTypeCreateInput inputVO) {
        Optional<RestaurantDishType> opDupDishType = restaurantDishTypeRepository.findFirstByName(inputVO.getName());
        if(opDupDishType.isPresent())
            throw exception(RESTAURANT_DISH_TYPE_EXISTS);
        RestaurantDishType newDishType = DishConvert.INSTANCE.dishTypeCreateInputConvert(inputVO);
        newDishType = restaurantDishTypeRepository.insert(newDishType);
        return newDishType.id();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean dishTypeUpdate(DishDishTypeUpdateInput inputVO) {
        Optional<RestaurantDishType> opDupDishType = restaurantDishTypeRepository.findFirstByNameAndIdNot(inputVO.getName(), inputVO.getId());
        if(opDupDishType.isPresent())
            throw exception(RESTAURANT_DISH_TYPE_EXISTS);
        RestaurantDishType updateDishType = DishConvert.INSTANCE.dishTypeUpdateInputConvert(inputVO);
        restaurantDishTypeRepository.update(updateDishType);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean dishTypeDelete(Long id) {
        Optional<RestaurantDishType> opDeleteDishType = restaurantDishTypeRepository.findById(id);
        if(!opDeleteDishType.isPresent())
            throw exception(RESTAURANT_DISH_TYPE_NOT_EXIST);
        restaurantDishTypeRepository.deleteById(id);
        return true;
    }

    @Override
    public DishSingleGetDishTypeOutput singleGetDishType(Long id) {
        Optional<RestaurantDishType> optionRestaurantDishType = restaurantDishTypeRepository.findById(id);
        if(!optionRestaurantDishType.isPresent()){
            throw exception(RESTAURANT_DISH_TYPE_NOT_EXIST);
        }
        return DishConvert.INSTANCE.singleGetDishTypeOutputConvert(optionRestaurantDishType.get());

    }

    @Override
    public List<DishListQueryDishTypeOutput> listQueryDishType() {
        List<RestaurantDishType> listRestaurantDishType = restaurantDishTypeRepository.listQueryDishType();
        return DishConvert.INSTANCE.listQueryDishTypeOutputConvert(listRestaurantDishType);

    }

    @Override
    public DishSingleGetOutput singleGet(Long id) {
        Optional<RestaurantDish> optionalRestaurantDish = restaurantDishRepository.findDetailById(id);
        if(!optionalRestaurantDish.isPresent())
            throw exception(DISH_NOT_EXISTS);
        return DishConvert.INSTANCE.singleGetDishOutputConvert(optionalRestaurantDish.get());
    }

    @Override
    public List<DishMultipleGetOutput> multipleGet(List<Long> ids) {
        return DishConvert.INSTANCE.multipleGetOutputConvert(restaurantDishRepository.findByIds(ids));
    }

}
